'use strict';
const xml2js = require('xml2js');
const crypto = require('crypto');
const getRawBody = require('raw-body');


const Service = require('egg').Service;
module.exports = class wxCryptService extends Service {
  async parseXML(xml) {
    const result = new Promise(function(resolve, reject) {
      xml2js.parseString(xml, {
        trim: true,
        explicitArray: false,
      }, function(err, result) {
        if (result) {
          resolve(result.xml);
        } else {
          reject(err);
        }
      });
    });
    return result;
  }
  async getXml(req) {
    return await getRawBody(req, {
      length: this.length,
      limit: '1mb',
      encoding: 'utf8',
    });
  }
  async buildXML(json) {
    const builder = new xml2js.Builder();
    return builder.buildObject(json);
  }

  async decrypt(str) {
    const aseKey = new Buffer(this.app.config.weChat.encodingAESKey + '=', 'base64');
    const iv = aseKey.slice(0, 16);
    const aesCipher = crypto.createDecipheriv('aes-256-cbc', aseKey, iv);
    aesCipher.setAutoPadding(false);
    let decipheredBuff = Buffer.concat([ aesCipher.update(str, 'base64'), aesCipher.final() ]);
    decipheredBuff = this.PKCS7Encoder(decipheredBuff);
    const len_netOrder_corpid = decipheredBuff.slice(16);
    const msg_len = len_netOrder_corpid.slice(0, 4).readUInt32BE(0);
    // recoverNetworkBytesOrder(len_netOrder_corpid.slice(0, 4));
    const result = len_netOrder_corpid.slice(4, msg_len + 4).toString();
    // const appId = len_netOrder_corpid.slice(msg_len + 4).toString();
    // console.log('appId');
    // console.log(appId);
    // console.log(this.app.config.weChat.appid);
    // if (appId !== this.app.config.weChat.appid) throw new Error('appId is invalid');
    return await this.parseXML(result);
  }
  PKCS7Encoder(buff) {
    const blockSize = 32;
    const strSize = buff.length;
    const amountToPad = blockSize - (strSize % blockSize);
    const pad = new Buffer(amountToPad - 1);
    pad.fill(String.fromCharCode(amountToPad));
    return Buffer.concat([ buff, pad ]);
  }
  async decryptMsg({ msg_signature, timestamp, nonce }, data) {
    const msg_encrypt = data.Encrypt;
    // if(data.ToUserName!=this.appID)throw new Error("ToUserName is invalid");
    if (this.getSignature(timestamp, nonce, msg_encrypt) !== msg_signature) throw new Error('msgSignature is not invalid');
    const decryptedMessage = await this.decrypt(msg_encrypt);
    return decryptedMessage;
  }
  getSignature() {
    const raw_signature = [ this.app.config.weChat.token, ...arguments ].sort().join('');
    const sha1 = crypto.createHash('sha1');
    sha1.update(raw_signature);
    return sha1.digest('hex');
  }
};
